<template>
  <div>
    <Modal v-model="modal" class="modal-full" title="规则校验" width=90%>
      <Spin size="large" fix v-show="false"></Spin>
      <div v-if="modalShow" class="rule-eval-box">
        <Row type="flex">
          <Col class="rule-box" :span="10">
          <div class="eval-tool">
            <div class="eval-select-box">
              <Selector @selectChange="ruleChange" :data="initRuleData" :initValue="initRuleData && initRuleData[0].id" label-icon="funnel" label="规则" :path="path.rule" :hasPage="true" :hasSearch="true"></Selector>
              <Selector @selectChange="dsChange" label-icon="cube" label="数据集" :path="path.dataset" :hasPage="true" :hasSearch="true"></Selector>
            </div>
          </div>
          <GridPanel ref="dataTable" :rowCheckMode="true" :hasCheck="false" :hasSearch="false" :hasAction="false" @rowCheck="dataCheck"  v-if="datasetModel" :single-select="true" size="small" class="data-list" :model="datasetModel">
          </GridPanel>
          <div class="empty-box center-container" v-else>暂无数据</div>
          </Col>
          <Col class="rule-box" :span="6">
          <SelectPanel class="list-rule" ref="detailList" title="匹配条件" :loading="ruleLoading" title-icon="pinpoint" :data="ruleShowList" @select="conditionSelectChange">
            <div class="" slot-scope="option" slot="option">
              <Badge dot v-if="singleEvalData && singleEvalData[option.index]"></Badge>
              {{option.item.condition_exp}}
              <div class="rule-memo" v-if="option.item.conditon_memo">{{option.item.conditon_memo}}</div>
            </div>
          </SelectPanel>
          </Col>

          <Col class="rule-box" :span="8">
          <div class="ivu-card ivu-card-bordered tree-card">
            <div class="ivu-card-head">
              <Icon type="network"></Icon>
              条件分解
            </div>
            <div class="ivu-card-body select-list-body">
              <Spin size="large" class="center-container" v-show="astLoading"></Spin>
              <!-- 本地分解逻辑 弃用
              <Tree :data="treeData" v-if="0">
                <template slot="node" slot-scope="item">
                  <div :class="extNodeClass(item.data && item.data.title)" v-html="expNodeName(item.data && item.data.title)">
                  </div>
                </template>
              </Tree>
              -->
              <Tree :data="astTreeData" v-if="astData">
                <template slot="node" slot-scope="item">
                  <Checkbox :value="item.data.result" disabled></Checkbox>
                  <div :class="extNodeClass(item)" v-html="expNodeName(item.data && item.data.title)">
                  </div>
                  <div v-if="item.data && item.data.children && !item.data.expand" class="result-title" v-html="expNodeName(item.data && item.data.expression)">
                  </div>
                </template>
              </Tree>
            </div>
          </div>

          </Col>
        </Row>
      </div>
      <div slot="footer" class="button-panel-button">
        <i-button @click="close">取消</i-button>
        <i-button @click="actionSubmit" type="primary">确认</i-button>
      </div>
    </Modal>
  </div>
</template>

<script>
import ModalBase from "../modalBase";
import Selector from "@/components/selector";
import Tree from "@/element/tree/index";
import SelectPanel from "@/components/selectPanel";
import DataTable from "@/components/gridTableSample";
import GridPanel from "@/node/gridPanel/index";

import $ from "@/common";
import { buildConditionTree, Operator } from "@/common/formula";
const expReg = {
  param: new RegExp("\\[\\'(.| )+?\\'\\]", "igm"),
  paramCode: new RegExp(/[\[\',\'\]]/gim)
};
export default {
  extends: ModalBase,
  props: {},
  data() {
    return {
      path: {
        find: "service/{app}/rule/{code}",
        rule: "/service/dw/rules",
        dataset: "service/{app}/bill/data/dataset/list",
        datasetList: "service/{app}/dataset/data/{code}",
        datasetEval: "service/{app}/rule/eval/dataset",
        singleEval: "service/{app}/rule/eval/single",
        ast: "service/{app}/rule/eval/ast",
        datasetModel: "/service/dw/dataset/model/{code}"
      },
      astData: null,
      detailIndex: null,
      ruleData: null,
      ruleCode: null,
      dataPageSize: 20,
      dataPage: 1,
      nullExp: "",
      evalLoading: false,
      //规则读取状态，包括读取列表和试算单条结果
      ruleLoading: false,
      //条件树读取状态
      astLoading: false,
      //试算返回原始数据
      matchedData: null,
      //试算数据对象，键为id，值为数据行
      evalData: null,
      //试算匹配对象，键为id,值为匹配条件index
      evalMatchData: null,
      datasetModel: null,
      datasetId: null,
      datasetCode: null,
      tableLoading: false,
      //选中的试算结果数据的序号，可通过evalData获取该行数据 通过evalMatchData 获取该行匹配的条件
      checkIndex: null,
      dsDataRow: null,
      singleEvalData: null,
      initRuleData: null
    };
  },
  computed: {
    treeData() {
      return [
        {
          title: "...",
          expand: true,
          ...buildConditionTree(
            this.ruleData && this.ruleData.details && this.detailIndex !== null
              ? this.ruleData.details[this.detailIndex].condition_exp ||
                this.nullExp
              : this.nullExp
          ),
          classes: null
        }
      ];
    },
    paramfields() {
      var data = {};
      if (this.ruleData.params && this.ruleData.params) {
        for (let field of this.ruleData.params) {
          data[field.code] = field;
        }
      }
      return data;
    },
    //记录表达式与标号关联
    ruleMap() {
      var map = {};
      if (this.ruleData && this.ruleData.details) {
        for (let i = 0; i < this.ruleData.details.length; i++) {
          map[this.ruleData.details[i].condition_exp] = i;
        }
      }
      return map;
    },
    astTreeData() {
      //列出变量，作为监听目标
      this.detailIndex;
      this.checkData;
      this.singleEvalData;
      var treeData = $.copy(this.astData);
      var counter = 1;
      $.eachChild({
        data: treeData,
        handler(data) {
          if (data.expression) {
            data.title = data.expression;
            if (
              data.expression[0] === "(" &&
              data.expression[data.expression.length - 1] === ")"
            ) {
              data.title = data.expression.slice(1, -1);
            }
            if (data.children) {
              data.title = data.name;
              data.expand = true;
              /*
              var temp = data.children.pop();       
              data.children.push({
                title: data.name
              });
              data.classes = ["node-express"];
              data.children.push(temp);
              */
            }
          } else {
          }
          data.name = counter++;
        }
      });
      treeData.name = 0;
      return [treeData];
    },
    ruleShowList() {
      return this.ruleData ? this.ruleData.details : [];
    },
    //合并计算结果队列
    evalList() {
      var list = [];
      for (let i in this.evalData) {
        list.push(this.evalData[i]);
      }
      return list;
    },
    checkData() {
      if (this.checkIndex !== null) {
        return this.$refs.dataTable.listData[this.checkIndex];
        //return this.evalMatchData[this.evalList[this.checkIndex].id];
      } else {
        return null;
      }
    }
  },
  watch: {
    checkData() {
      this.singleEval();
    },
    ruleData() {
      this.singleEval();
    }
  },
  created() {},
  methods: {
    load() {
      //清空状态
      this.detailIndex = null;
      this.ruleData = null;
      this.ruleCode = null;
      this.matchedData = null;
      this.evalData = null;
      this.evalMatchData = null;
      this.datasetId = null;
      this.datasetCode = null;
      //选中外部列表中的规则时，则将选中规则带入选择栏中
      if (this.actionData && this.actionData.id) {
        this.ruleCode = this.actionData.code;
        this.$set(this, "initRuleData", [this.actionData]);
        this.ruleChange(this.actionData);
      } else {
        this.$set(this, "initRuleData", null);
      }
    },
    conditionSelectChange(index) {
      this.detailIndex = index;
      this.$set(this, "astData", null);
      this.updateAstData();
      //远程获取属性数据
    },
    //远程获取属性数据
    updateAstData() {
      if (this.detailIndex === null && this.detailIndex === undefined) return;
      this.astLoading = true;
      $.link(this.path.ast, {
        expression: this.ruleData.details[this.detailIndex].condition_exp,
        param: this.checkData || {}
      })
        .then(res => {
          this.$set(this, "astData", res.data);
        })
        .finally(() => {
          this.astLoading = false;
        });
    },
    expNodeName(str) {
      if (Operator[str] && Operator[str].name) {
        return Operator[str].name + " " + Operator[str].code;
      } else {
        //字符串变量处理
        return this.replaceParam(str);
      }
    },
    //本地试算单一数据
    singleEval() {
      if (!this.ruleData || !this.checkData) {
        this.$set(this, "singleEvalData", null);
        return;
      }
      this.ruleLoading = true;
      $.link(this.path.singleEval, {
        rule_id: this.ruleData.id,
        data: this.checkData
      })
        .then(res => {
          var data = {};
          for (let i = 0; i < res.data.matched_details.length; i++) {
            let condition = res.data.matched_details[i];
            let ruleIndex = i;
            for (let j = 0; j < this.ruleData.details.length; j++) {
              var rule = this.ruleData.details[j];
              if (rule.id === condition.id) {
                ruleIndex = j;
                break;
              }
            }
            data[ruleIndex] = true;
          }
          this.$set(this, "singleEvalData", data);
          this.updateAstData();
        })
        .finally(() => {
          this.ruleLoading = false;
        });
    },
    //与条件计算组件处理类似，待合并
    replaceParam(str) {
      if (str && typeof str === "string") {
        return str
          .replace(
            /(\bor\b|\band\b|\>\=|\<\=|\|\||\=\=|\!\=|\&\&|[\<\>\+\-\!\*/\(\)])/gim,
            (str, value) => {
              //操作符替换,需要先行进操作符替换，否则会导致html被解析为操作符
              var opt = Operator[value];
              var classes = "item-operator";
              if (opt && opt.condition) {
                classes += " item-condition";
              }
              return `<span class="${classes}">${opt
                ? opt.name || value
                : value}</span>`;
            }
          )
          .replace(expReg.param, s => {
            var code = s
              .replace(expReg.paramCode, "")
              .replace(/^\s+|\s+$/gm, "");
            return `<span class="item-param">${this.paramfields[code]
              ? this.paramfields[code].name
              : code}</span>`;
          });
      } else {
        return str;
      }
    },
    //执行试算
    doEval() {
      this.evalLoading = true;
      $.link(this.path.datasetEval, {
        dataset_id: this.datasetId,
        rule_id: this.ruleCode,
        pageNo: this.dataPage,
        pageSize: this.dataPageSize
      })
        .then(res => {
          this.setMatchedData(res.data.matched_details);
        })
        .finally(() => {
          this.evalLoading = false;
        });
    },
    //选中试算结果
    dataCheck(e) {
      ///先等待grid组件的状态响应
      this.$nextTick(() => {
        var index = this.$refs.dataTable.getSelectedIndex();
        if (index !== undefined) {
          this.checkIndex = index;
        }
      });
    },
    setMatchedData(data) {
      this.matchedData = data;
      var evalMatchData = {};
      var evalData = {};
      for (let i = 0; i < this.matchedData.length; i++) {
        let condition = this.matchedData[i];
        let ruleIndex = i;
        for (let j = 0; j < this.ruleData.details.length; j++) {
          var rule = this.ruleData.details[j];
          if (rule.id === condition.id) {
            ruleIndex = j;
            break;
          }
        }
        if (condition.matched_data) {
          for (let row of condition.matched_data) {
            evalData[row.id] = row;
            if (!evalMatchData[row.id]) {
              evalMatchData[row.id] = {};
            }
            evalMatchData[row.id][ruleIndex] = true;
          }
        }
      }
      this.checkIndex = null;
      this.$set(this, "evalData", evalData);
      this.$set(this, "evalMatchData", evalMatchData);
      this.updateEvalTable();
    },
    updateEvalTable(evalData) {
      this.tableLoading = true;
      $.link(this.path.datasetModel, null, {
        method: "get",
        param: {
          code: this.datasetCode
        }
      })
        .then(res => {
          this.tableLoading = false;
          var model = res.data || {};
          //根据数据创建简单model
          if ((!model.fields || !model.fields.length) && this.evalList.length) {
            model.fields = [];
            for (let i in this.evalList[0]) {
              model.fields.push({
                code: i,
                name: i
              });
            }
          }
          this.$set(this, "datasetModel", model);
        })
        .finally(() => {
          this.tableLoading = false;
        });
    },
    //根据节点数据生成节点类
    extNodeClass(item) {
      var str = item.data && item.data.title;
      var data = item.data;
      var classes = [];
      if (data.result !== undefined && data.result !== null) {
        classes.push("result-" + data.result.toString());
      }
      if (data.children) {
        classes.push("result-title");
      }
      if (Operator[str] || str === "and" || str === "or") {
        if (
          str === "and" ||
          str === "or" ||
          (Operator[str] && Operator[str].name)
        ) {
          classes.push("char-operator");
        } else {
          classes.push("char-condition");
        }
        var c = Operator[str] ? Operator[str].code : str;
        switch (c) {
          case "and":
            classes.push("b-blue");
            break;
          case "or":
            classes.push("b-azure");
            break;
        }
      }
      return classes;
    },
    dsChange(data) {
      if (!data) return;
      this.datasetId = data.id;
      this.datasetCode = data.code;
      this.fetchDsModel();
    },
    fetchDsModel() {
      this.checkIndex = null;
      this.$set(this, "datasetModel", null);
      $.link(this.path.datasetModel, null, {
        method: "get",
        param: {
          code: this.datasetCode
        }
      }).then(({ data }) => {
        this.$set(this, "datasetModel", {
          path: $.composePath(this.path.datasetList, {
            code: this.datasetCode
          }),
          actions: [],
          fields: data.fields
        });
      });
    },
    //读取规则明细
    loadRule() {
      //this.checkIndex = null;
      this.ruleLoading = true;
      $.link(this.path.find, null, {
        method: "get",
        param: {
          code: this.ruleCode
        }
      })
        .then(res => {
          this.detailIndex = null;
          this.$set(this, "ruleData", res.data);
          this.$refs.detailList && this.$refs.detailList.clearSelect();
        })
        .finally(() => {
          this.ruleLoading = false;
        });
    },
    ruleChange(data) {
      if (!data) return;
      this.ruleCode = data.code;
      this.loadRule();
    },
    actionSubmit() {}
  },
  components: {
    Selector,
    SelectPanel,
    Tree,
    DataTable,
    GridPanel
  }
};
</script>

<style lang="less">
.rule-eval-box {
  .ivu-row-flex {
    height: 100%;
    > .rule-box {
      display: flex;
      flex-direction: column;
      height: 100%;
      .data-list {
        flex: 1;
      }
    }
  }
  .ivu-spin.center-container {
    width: 100%;
    height: 100%;
  }
  .eval-box {
    display: flex;
    border-style: solid;
    border-color: #dddee1;
    border-width: 0 1px;
    .ivu-input-wrapper {
      flex: 1;
      .ivu-input {
        border-radius: 0;
      }
    }
    .ivu-btn {
      border-radius: 0;
      border: 0;
    }
    .label {
      background: #eee;
      padding: 6px 12px;
    }
  }
  .rule-box {
    padding: 0 8px;
    .eval-select-box {
      display: flex;
      > div {
        flex: 1;
      }
    }
    .selector-has-label {
      .selecor-label {
        border-radius: 0px;
      }
      .ivu-select {
        .ivu-select-selection {
          border-radius: 0px;
        }
      }
    }
    .ivu-card.tree-card {
      height: 100%;
      display: flex;
      flex-direction: column;
      .ivu-card-head {
        border-bottom: 1px solid #e9eaec;
        padding: 8px 12px;
        font-size: 14px;
      }
      .ivu-card-body {
        padding: 0;
        overflow: auto;
        flex: 1;
      }
      box-shadow: none;
    }
    .ivu-tree {
      .ivu-checkbox-disabled.ivu-checkbox-checked .ivu-checkbox-inner {
        border-color: #83e8a8;
        background-color: #96e6b3;
      }
      .ivu-checkbox-disabled.ivu-checkbox-checked .ivu-checkbox-inner:after {
        animation-name: none;
        border-color: #fff;
      }
      /*
      border: 1px solid #e9eaec;
      border-radius: 4px;
      padding-bottom: 8px;
      overflow: auto;
      */
      > .ivu-tree-children {
        > li > .tree-item {
          /*
          border-bottom: 1px solid #e9eaec;
          font-size: 14px;
          margin-bottom: 8px;
          padding: 5px 12px;
          */
        }
      }
    }
  }
  .rule-memo {
    padding-left: 12px;
    color: #ababab;
  }
  .ivu-badge {
    .ivu-badge-dot {
      top: -10px;
      right: -2px;
      height: 6px;
      width: 6px;
    }
  }
  .data-list {
    .table-data-wrapper {
      border-style: solid;
      border-color: #dddee1;
      border-width: 0 1px;
      .table {
        margin: 0;
        tr.active td {
          background-color: #e9f7ff;
        }
      }
    }
  }
  .result-true {
    border-radius: 2px;
    padding: 2px 4px;
    /*border-left: 8px solid #4fd593;*/
    display: inline-block;
  }
  .result-false {
    border-radius: 2px;
    padding: 2px 8px;
    /*border-left: 8px solid #d54f4f;*/
    display: inline-block;
  }
  .result-title {
    margin-left: 4px;
    padding: 2px 8px;
    display: inline-block;
  }
  .char-operator {
    border-radius: 2px;
    color: #fff;
    font-weight: bold;
    padding: 0px 16px;
  }
  .char-operator {
    border-radius: 2px;
    color: #fff;
    font-weight: bold;
    padding: 0px 16px;
  }
  .char-condition {
    border-radius: 20px;
    background: #4fd593;
    color: #fff;
    .item-operator {
      color: #fff;
      padding: 0;
    }
    padding: 0px 8px;
    font-size: 16px;
  }
  .ivu-tree-children .tree-item {
    &.tree-item-selected {
      & ~ .ivu-tree-children {
        /*background: #d5e8fc;*/
      }
    }
    &:hover {
      & ~ .ivu-tree-children,
      & ~ .node-express .ivu-tree-children {
        /*background: #eaf4fe;*/
      }
    }
  }
  .empty-box {
    flex: 1;
    font-size: 12px;
    border: solid 1px #e9eaec;
    border-top-width: 0;
  }
  .list-rule {
    display: flex;
    flex-direction: column;
    height: 100%;
    .ivu-card-head {
      border-bottom: none;
      padding: 8px 12px;
      font-size: 14px;
    }
    .ivu-card-body {
      flex: 1;
      overflow: auto;
    }
  }
  .item-param {
    color: #2ac2e6;
    padding-left: 4px;
    padding-right: 4px;
  }
  .item-operator {
    color: #2196f3;
    padding-left: 1px;
    padding-right: 1px;
    &.item-condition {
      padding: 1px 4px;
      margin: 0 2px;
      border-radius: 2px;
      border: 1px solid #2196f3;
    }
  }
  .ivu-tree ul > li {
    margin: 0;
  }
  .node-express {
    > li {
      position: relative;
      > .ivu-tree-children ~ .tree-line,
      > .node-express ~ .tree-line {
        width: 3px;
        border-width: 0px 0 1px 1px;
        border-style: dotted;
        border-color: #c1c1c1;
        margin-top: 10px;
        position: absolute;
        left: 14px;
        top: 16px;
        bottom: 12px;
      }
    }
    li > .tree-item + .ivu-tree-children,
    li > .tree-item + .node-express {
      margin-top: 0;
    }
    > li > .tree-item > .ivu-tree-title::before {
      content: "...";
    }
    > li > .tree-item > .ivu-tree-arrow-open + .ivu-tree-title::before {
      content: none;
    }
  }
}
</style>
